//    Copyright (C) Kherty.  All rights reserved.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.Text;
using System.Windows;
using System.Windows.Media;
using OpenLS.Core;
using OpenLS.Core.Serialization;
using OpenLS.Spreadsheet.Internals;

namespace OpenLS.Spreadsheet
{
    internal abstract partial class FormattingRecord : INotifyPropertyChanged, IOfficeXmlSerializable
    {

        public virtual void CopyFrom(FormattingRecord other)
        {
            Contract.Requires(other != null);
            this._textRotation = other._textRotation;
            if (other._format != null)
                this._format = other._format;
            if (other._font != null)
                this._font = other.Font.Clone(); //\\ check this
            if (other._background != null)
                this._background = other.Background.Clone(); //\\check this
#if DEBUG
            object dummy = other.Background;
            object dummy2 = this.Background;
#endif
            if (other._borders != null)
                this._borders = other.Borders.Clone();
            this._horizontalAlignment = other._horizontalAlignment;
            this._verticalAlignment = other._verticalAlignment;
            this._textWrapping = other._textWrapping;
            this._indentation = other._indentation;
            this._hashCodeCache = null;
        }

        internal bool IsCellFormatting { get { return true; } } //\\temp
#if DEBUG
        /* public override string ToString()
        {
            return StringOf(_borders, _font, _format, _horizontalAlignment, 0, _indentation, _isHidden, _isLocked,
                            _verticalAlignment, _background, _parent, _textRotation, _textWrapping);
        }*/

        internal static string StringOf(params object[] oo)
        {
            var sb = new StringBuilder();
            foreach (object o in oo)
            {
                if (o == null)
                    sb.Append("null");
                else
                    sb.Append(o.ToString());
                sb.Append("\n");
            }
            return sb.ToString();
        }
#endif

        internal void SetCellValue(CellProperty property, object value)
        {
            ResetHashCode();
            switch (property)
            {
                    /*                case CellProperty.Fill:
                                    if (value == null)
                                        Background = null;
                                    else
                                    {
                                        //\\Fill = new PatternFill (this.StyleSheet, (Brush)value);
                                        Background = (Brush)value;
                                    }
                                    break;
                  */
                case CellProperty.Foreground:
                    object v = value;
                    if (v is Color)
                        v = (SpreadsheetColor) (Color) v;
                    Foreground = (SpreadsheetColor) v;
                    break;
                case CellProperty.Background:
                    Background = (Fill) value;
                    break;
                case CellProperty.FontFamily:
                    if (_font == null)
                        _font = new Font();
                 //   if (value == null)
                        _font.FontFamily = null;
                   // else
                    {
                        _font = _font.CloneExcept(f => f.FontFamily = (FontFamily) value);
                    }
                    break;
                case CellProperty.FontSize:
                    if (_font == null)
                        _font = new Font();
                    _font = _font.CloneExcept(f => f.Size = (double) value);
                    //_font.Size = (double) value;
                    break;

                case CellProperty.FontStyle:
                    if (_font == null)
                        _font = new Font();
                    _font = _font.CloneExcept(f => f.FontStyle = (FontStyle) value);
//\\                    _font.FontStyle = (FontStyle) value;
                    break;
                case CellProperty.FontUnderline:
                    if (_font == null)
                        _font = new Font();
                    _font = _font.CloneExcept(f => f.Underline = (UnderlineType) value);
                    //\\_font.Underline = (UnderlineType) value;
                    break;
                case CellProperty.FontWeight:
                    if (_font == null)
                        _font = new Font();
                    _font = _font.CloneExcept(f => f.FontWeight = (FontWeight) value);
                    //\\_font.FontWeight = (FontWeight) value;
                    break;
                case CellProperty.HorizontalAlignment:
                    HorizontalAlignment = (HorizontalCellAlignment) value;
                    break;
                case CellProperty.Indentation:
                    Indentation = (int) value;
                    break;
                case CellProperty.Format:
                    Format = (Format) value;
                    break;
                case CellProperty.TextWrapping:
                    TextWrapping = (TextWrapping) value;
                    break;
                case CellProperty.VerticalAlignment:
                    VerticalAlignment = (VerticalCellAlignment) value;
                    break;
                case CellProperty.Borders:
                    Borders = (Borders) value;
                    break;
                case CellProperty.IsHidden:
                    IsHidden = (bool) value;
                    break;
                case CellProperty.IsLocked:
                    IsLocked = (bool) value;
                    break;
                case CellProperty.Rotation:
                    TextRotation = (int) value;
                    break;
                case CellProperty.IsVertical:
                    
                    throw new NotImplementedException();
                case CellProperty.Max:
                    throw new NotSupportedException();
                    
                default:
                    throw new NotSupportedException();
            }
            ResetHashCode();
        }

        public FontStyle FontStyle
        {
            get
            {
                if (_font == null)
                {
                    if (Parent != null)
                        return Parent.FontStyle;
                    return FontStyles.Normal;
                }
                return _font.FontStyle;
            }
            set {
                _font = CloneFontExcept(f => f.FontStyle = value);
                notifyPropertyChanged(CellProperty.FontSize);
            }
        }

        /*public UnderlineType FontUnderline
        {
            get
            {
                if (_font == null)
                {
                    if (Parent != null)
                        return Parent.FontUnderline;
                    return UnderlineType.None;
                }
                return _font.Underline;
            }
        }*/

        public FontWeight FontWeight
        {
            get
            {
                if (_font == null)
                {
                    if (Parent != null)
                        return Parent.FontWeight;
                    return FontWeights.Normal;
                }
                return _font.FontWeight;
            }
            set
            {
                _font = CloneFontExcept(f => f.FontWeight = value);
                notifyPropertyChanged(CellProperty.FontWeight);
            }
        }

        /*public Brush Background
        {
            get
            {
                if (fill == null && parent != null)
                    return parent.Background;
                if (fill != null)
                    return fill.Brush;
                return null;
            }
            set
            {
                if (value == null)
                    fill = null;
                else
                {
                    fill = new PatternFill(styleSheet, value);
                }
            }
        }*/
        //\\
        public FontFamily FontFamily
        {
            get
            {
                if (Font == null && Parent != null)
                    return Parent.FontFamily;
                if (_font != null)
                    return _font.FontFamily;
                return null;
            }
            set
            {
#if DEBUG
                Contract.Assert(!IsFrozen);
#endif
                _font = CloneFontExcept(f => f.FontFamily = value);
                notifyPropertyChanged(CellProperty.FontFamily);
                ResetHashCode();

            }
        }

        internal int Id { get; set; } //\\ remove!!

        private HorizontalCellAlignment? _horizontalAlignment;

        public HorizontalCellAlignment? HorizontalAlignment
        {
            get
            {
                if (_horizontalAlignment.HasValue)
                    return _horizontalAlignment.Value;
                if (!applyAlignment && Parent != null) return Parent.HorizontalAlignment;
                return null;
            }
            set
            {
                _horizontalAlignment = value;
                applyAlignment = CanApply;
                ResetHashCode();
            }
        }

        protected int _textRotation;

        public int TextRotation
        {
            get { return _textRotation; }
            internal set
            {
                _textRotation = value;
                applyAlignment = CanApply;
                ResetHashCode();
            }
        }

        private VerticalCellAlignment? _verticalAlignment;

        public VerticalCellAlignment VerticalAlignment //\\ make nullable?
        {
            get
            {
                if (_verticalAlignment.HasValue)
                    return _verticalAlignment.Value;
                if (Parent != null)
                    return Parent.VerticalAlignment;
                return VerticalCellAlignment.Bottom;
            }
            set
            {
                _verticalAlignment = value;
                applyAlignment = CanApply;
                ResetHashCode();
            }
        }

        private TextWrapping? _textWrapping;

        public TextWrapping TextWrapping //\\ make nullable?
        {
            get
            {
                if (_textWrapping.HasValue)
                    return _textWrapping.Value;
                if (Parent != null)
                    return Parent.TextWrapping;
                return TextWrapping.NoWrap;
            }
            set
            {
                _textWrapping = value;
                applyAlignment = CanApply;
                ResetHashCode();
            }
        }

        private double? _indentation;

        public double Indentation
        {
            get
            {
                if (_indentation.HasValue)
                    return _indentation.Value;
                if (Parent != null)
                    return Parent.Indentation;
                return 0;
            }
            set
            {
                _indentation = value;
                applyAlignment = CanApply;
                ResetHashCode();
            }
        }

        /*public ColorReference FontColor
        {
            get
            {
                if (Font == null)
                {
                    if (Parent != null)
                        return Parent.FontColor;
                    return new RgbColorReference(Colors.Black); ;
                }
                return Font.Color;
            }
            set
            {
                if (Font == null)
                {
                    font = new Font(styleSheet);
                }
                font.Color = value;
            }
        }*/

        public SpreadsheetColor Foreground
        {
            get
            {
                if (Font == null)
                {
                    if (Parent != null)
                        return Parent.Foreground;
                    return Colors.Black;
                }
                return Font.Foreground;
            }
            set
            {
                Contract.Assert(!(value != null && value.ToString().Contains("FFFFC7CE")));
                _font = CloneFontExcept(f => f.Foreground = value);
                notifyPropertyChanged(CellProperty.Foreground);

            }
        }

        private Font CloneFontExcept(Action<Font> func)
        {
            Internals.Font result;
            if (_font == null)
            {
                result = new Font();
#if DEBUG
                result.UnFreeze();
#endif
                func(result);
#if DEBUG
                result.Freeze();
#endif
            }
            else
            {
                result = _font.CloneExcept(func);
            }
            return result;
        }


        

        private Format _format;

        internal Format RawFormat { get { return _format; } }
        internal Font RawFont { get { return _font; } }
        internal Borders RawBorders { get { return _borders; } }
        internal Fill RawBackground { get { return _background; } }
        public Format Format
        {
            get
            {
                if (applyNumberFormat || Parent == null)
                    return _format;
                return Parent.Format;
            }
            set
            {
                _format = value;
                applyNumberFormat = CanApply;
                ResetHashCode();
            }
        }

        private Font _font;

        public Font Font
        {
            get
            {
                if (_font != null)
                    return _font;
                if (Parent!= null)
                    return Parent.Font;
                return _font;
            }
            set
            {
                _font = value;
                applyFont = CanApply;
                ResetHashCode();
            }
        }

        private Fill _background;

        public Fill Background
        {
            get
            {
                Contract.Ensures(
    !(Contract.Result<Fill>() is GradientFill &&
      ((GradientFill)Contract.Result<Fill>()).Stops.Count == 0));

                if (ApplyFill || Parent == null)
                    return _background;
                return Parent.Background;
            }
            set
            {
                Contract.Assert(
!(value is GradientFill &&
((GradientFill)value).Stops.Count == 0));
                _background = value;
                applyFill = true;
                ResetHashCode();
                notifyPropertyChanged(CellProperty.Background);
            }
        }

        internal bool ApplyFill
        {
            get
            {
                return applyFill;
            }
        }


        public double FontSize
        {
            get
            {
                if (_font != null)
                    return _font.Size;
                return 11;
            }
            set
            {
                _font = CloneFontExcept(f => f.Size = value);

                notifyPropertyChanged(CellProperty.FontSize);
            }
        }

        private Borders _borders;

        public Borders Borders
        {
            get
            {
                if (applyBorder || Parent == null)
                    return _borders;
                return Parent.Borders;
            }
            set
            {
                _borders = value;
                applyBorder = CanApply;
                ResetHashCode();
            }
        }
        bool CanApply { get { return Parent != null; } }

        private int? _hashCodeCache;
#if DEBUG
        private string hashTrace;
        bool IsFrozen { get { return _hashCodeCache != null; } }
#endif

        public override int GetHashCode()
        {
            if (_hashCodeCache == null)
            {
                _hashCodeCache = getHashCodeImp();
#if DEBUG2
                hashTrace = GetHashCodeTrace();
#endif
            }
#if DEBUG2
            if (hashTrace != GetHashCodeTrace())
            {
                Debug.WriteLine("------------------");
                Debug.WriteLine(hashTrace);
                Debug.WriteLine(GetHashCodeTrace());
                Debug.WriteLine("------------------");
            }
            Contract.Assert(hashTrace == GetHashCodeTrace());
            Contract.Assert(getHashCodeImp() == getHashCodeImp());
            Contract.Assert(getHashCodeImp() == getHashCodeImp());
            Contract.Assert(_hashCodeCache.Value == getHashCodeImp());
#endif
            return _hashCodeCache.Value;
        }
#if DEBUG
        public string GetHashCodeTrace()
        {

            string result = "";
            result += "rotation " +  _textRotation.GetHashCode();
            result += pivotButton.GetHashCode() + quotePrefix.GetHashCode() + applyAlignment.GetHashCode() +
                      applyBorder.GetHashCode() + applyFill.GetHashCode() +
                      applyFont.GetHashCode() + applyNumberFormat.GetHashCode() + applyProtection.GetHashCode();

            if (_format != null)
                result += "format ( " + _format.GetHashCode() + " )";
            if (_font != null)
                result += "font " + _font.GetHashCode() + _font.GetHashTrace();
            if (_background != null)
                result +=  "background " + _background.GetHashCode();
            if (_borders != null)
                result += "borders " + _borders.GetHashCode();
            if (_horizontalAlignment != null)
                result += "horizontalAlignment " + _horizontalAlignment.Value.GetHashCode();
            if (_verticalAlignment != null)
                result += "verticalAlignment " + _verticalAlignment.Value.GetHashCode();
            if (_textWrapping != null)
                result += "TextWrapping" + _textWrapping.Value.GetHashCode();
            result += "Indentation " + _indentation.GetHashCode();
            if (Parent != null)
                result += "Parent " + Parent.GetHashCode();
            result += "IsLocked " + IsLocked.GetHashCode();
            result += "IsHidden " + IsHidden.GetHashCode();
            return result;
        }

        private int getHashCodeImp1()
        {
            int result = _textRotation.GetHashCode();
            result += pivotButton.GetHashCode() + quotePrefix.GetHashCode() + applyAlignment.GetHashCode() +
                      applyBorder.GetHashCode() + applyFill.GetHashCode() +
                      applyFont.GetHashCode() + applyNumberFormat.GetHashCode() + applyProtection.GetHashCode();

            if (_format != null)
                result += _format.GetHashCode();
            if (_font != null)
                result += _font.GetHashCode();
            return result;
        }
#endif

        private int getHashCodeImp()
        {
            int result = _textRotation.GetHashCode();
            result += pivotButton.GetHashCode() + quotePrefix.GetHashCode() + applyAlignment.GetHashCode() +
                      applyBorder.GetHashCode() + applyFill.GetHashCode() +
                      applyFont.GetHashCode() + applyNumberFormat.GetHashCode() + applyProtection.GetHashCode();

            if (_format != null)
                result += _format.GetHashCode();
            if (_font != null)
                result += _font.GetHashCode();
            if (_background != null)
                result += _background.GetHashCode();
            if (_borders != null)
                result += _borders.GetHashCode();
            if (_horizontalAlignment != null)
                result += _horizontalAlignment.Value.GetHashCode();
            if (_verticalAlignment != null)
                result += _verticalAlignment.Value.GetHashCode();
            if (_textWrapping != null)
                result += _textWrapping.Value.GetHashCode();
            result += _indentation.GetHashCode();
            if (Parent != null)
                result += Parent.GetHashCode();
            result += IsLocked.GetHashCode();
            result += IsHidden.GetHashCode();
            return result;
        }

        
        public bool EqualsRecord(FormattingRecord other)
        {
            Contract.Ensures(Contract.Result<bool>() || other != this);
          //  Contract.Ensures(!Contract.Result<bool>() || GetHashCode() == other.GetHashCode());
             Contract.Ensures(!Contract.Result<bool>() || getHashCodeImp() == other.getHashCodeImp());
#if DEBUG
            var test = Equals(_background, other._background);
#endif
            bool result =
                pivotButton == other.pivotButton &&
                quotePrefix == other.quotePrefix &&
                applyAlignment == other.applyAlignment &&
                applyBorder == other.applyBorder &&
                applyFill == other.applyFill &&
                applyFont == other.applyFont &&
                applyNumberFormat == other.applyNumberFormat &&
                applyProtection == other.applyProtection &&
                Equals(_textRotation, other._textRotation) &&
                Equals(_format, other._format) &&
                Equals(_font, other._font) &&
                Equals(_background, other._background) &&
                Equals(_borders, other._borders) &&
                Equals(_horizontalAlignment, other._horizontalAlignment) &&
                Equals(_verticalAlignment, other._verticalAlignment) &&
                Equals(_textWrapping, other._textWrapping) &&
                Equals(_indentation, other._indentation) &&
                (Parent == null) == (other.Parent == null) &&
                // Equals(Parent, record.Parent) &&
                IsLocked == other.IsLocked &&
                IsHidden == other.IsHidden;
            if (result && Parent != null)
            {
                // Contract.Assert(other.Parent != null);
                bool otherParentNotNull = (other.Parent != null);
                if (!otherParentNotNull)
                    return false;
                result = Parent.Equals(other.Parent);
            }
#if DEBUG
            if (result && getHashCodeImp() != other.getHashCodeImp())
            {
                int i = this.getHashCodeImp();
                int j = other.getHashCodeImp();
                // Contract.Assert(false);
            }
#endif
            return result;
        }

        internal const bool IsLockedDefault = true;
        private bool _isLocked = IsLockedDefault;

        public bool IsLocked
        {
            get
            {
                if (applyProtection || Parent == null)
                    return _isLocked;
                if (Parent != null)
                    return Parent.IsLocked;
                return IsLockedDefault;
            }
            set
            {
                _isLocked = value;
                applyProtection = CanApply;
                ResetHashCode();
            }
        }

        private void ResetHashCode()
        {
            ///\\\for now Contract.Assert(_hashCodeCache == null);
            _hashCodeCache = null;
        }

        private bool _isHidden = IsHiddenDefault;

        private bool applyFill,
                     applyFont,
                     applyBorder,
                     applyNumberFormat,
                     applyAlignment,
                     applyProtection;
        internal void ApplyForParent()
        {
            if (this.Background != null)
                applyFill = true;
            if (this.Font != null)
                applyFont = true;
            if (this.Borders != null) this.applyBorder = true;
            if (this.Format != null) this.applyNumberFormat = true;
            //\\todo : protection and alignment?
        }

        private bool quotePrefix, pivotButton;

        private const bool IsHiddenDefault = false;

        public bool IsHidden
        {
            get
            {
                if (applyProtection || Parent == null)
                    return _isHidden;
                if (Parent != null)
                    return Parent.IsHidden;
                return IsHiddenDefault;
            }
            set
            {
                _isHidden = value;
                applyProtection = true;
                ResetHashCode();
            }
        }


        internal void ClearValue(CellProperty p)
        {
            switch (p)
            {
                case CellProperty.Indentation:
                    _indentation = null;
                    break;
                default:
                    throw new NotImplementedException();
            }
        }

#if DEBUG
        public int tag;
#endif

        public abstract StyleFormattingRecord Parent
        { get;
        }


        internal object GetCellValue(CellProperty property)
        {
            Contract.Ensures(!(Contract.Result<object>() is Exception));
            switch (property)
            {
                case CellProperty.Foreground:
                    return Foreground;
                case CellProperty.Background:
                    return Background;
                case CellProperty.FontFamily:
                    if (_font == null)
                        return null;
                    return _font.FontFamily;
                case CellProperty.FontSize:
                    if (_font == null)
                        return 11d; //\\? move to constant
                    return _font.Size;
                case CellProperty.FontStyle:
                    if (_font == null)
                        return FontStyles.Normal;
                    return _font.FontStyle;
                case CellProperty.FontUnderline:
                    if (_font == null)
                        return UnderlineType.None;
                    return _font.Underline;
                case CellProperty.FontWeight:
                    if (_font == null)
                        return FontWeights.Normal;
                    return _font.FontWeight;
                case CellProperty.HorizontalAlignment:
                    return HorizontalAlignment;
                case CellProperty.Indentation:
                    return Indentation;
                case CellProperty.Format:
                    return Format;
                case CellProperty.TextWrapping:
                    return TextWrapping;
                case CellProperty.VerticalAlignment:
                    return VerticalAlignment;
                case CellProperty.IsLocked:
                    return this.IsLocked;
                case CellProperty.IsHidden:
                    return this.IsHidden;
                    case CellProperty.Rotation:
                    return this.TextRotation;
                    case CellProperty.Borders:
                    case CellProperty.IsVertical:case CellProperty.Max:
                    throw new NotImplementedException();
                default:
                    throw new NotSupportedException();
            }
        }

        public string FormatCode
        {
            get
            {
                if (Format == null)
                    return "General";
                return Format.Code;
            }
            set
            {
                Format = Format.Parse(value);
                ResetHashCode();
            }
        }

        internal bool ApplyBorder
        {
            get
            {
                Contract.Ensures(!(Contract.Result<bool>() && Parent == null));
                return applyBorder;
            }
        }

        internal bool ApplyAlignment
        {
            get
            {
                Contract.Ensures(!(Contract.Result<bool>() && Parent == null));
                return applyAlignment;
            }
        }


        internal bool ApplyFont
        {
            get
            {
                Contract.Ensures(!(Contract.Result<bool>() && Parent == null));
                return applyFont;
            }
        }

        internal bool ApplyNumberFormat
        {
            get
            {
                Contract.Ensures(!(Contract.Result<bool>() && Parent == null));
                return applyNumberFormat;
            }
        }

        internal bool ApplyProtection
        {
            get
            {
                Contract.Ensures(!(Contract.Result<bool>() && Parent == null));
                return applyProtection;
            }
        }

        internal UnderlineType FontUnderline
        {
            get
            {
                if (Font == null)
                {
                    if (Parent != null)
                        return Parent.FontUnderline;
                    return UnderlineType.None;
                }
                return Font.Underline;
            }
            set
            {
                _font = CloneFontExcept(f => f.Underline = value);
                notifyPropertyChanged(CellProperty.FontUnderline);

            }
        }

        public FontSchemeType FontScheme
        {
            get { if (_font == null)
            {
                if (Parent != null)
                    return Parent.FontScheme;
                return FontSchemeType.None;
            }
                return _font.Scheme;
            }
            set
            {
                _font = CloneFontExcept(f => f.Scheme = value);
                notifyPropertyChanged(CellProperty.FontFamily);
            }
        }


        internal static object GetDefault(CellProperty p)
        {
            Contract.Ensures(!(Contract.Result<object>() is Exception));

            switch (p)
            {
                case CellProperty.Background:
                case CellProperty.Borders:
                case CellProperty.FontFamily:
                case CellProperty.Foreground:
                case CellProperty.Format:
                    return null;
                case CellProperty.VerticalAlignment:
                    return VerticalCellAlignment.Center;
                case CellProperty.TextWrapping:
                    return TextWrapping.NoWrap;
                case CellProperty.FontSize:
                    return 11d; //\\
                case CellProperty.FontUnderline:
                    return UnderlineType.None;
                case CellProperty.FontStyle:
                    return FontStyles.Normal;
                case CellProperty.FontWeight:
                    return FontWeights.Normal;
                case CellProperty.HorizontalAlignment:
                    return null; //\\
                case CellProperty.Indentation:
                    return 0;
                case CellProperty.IsLocked:
                    return true;
                    case CellProperty.IsHidden:case CellProperty.IsVertical:case CellProperty.Max:case CellProperty.Rotation:
                    throw new NotImplementedException();
                default:
                    throw new NotSupportedException();
            }
            //\\throw new NotImplementedException();
        }

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        private void notifyPropertyChanged(CellProperty propertyName)
        {
            ResetHashCode();
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName.ToString()));
        }

        #endregion

        #region IOfficeXmlSerializable Members

        void IOfficeXmlSerializable.ReadXml(ReadContext context)
        {
            using (ReadContext c = context.Read("xf"))
            {
                pivotButton = c.GetOptionalBoolean("pivotButton", false);
                quotePrefix = c.GetOptionalBoolean("quotePrefix", false);
                applyAlignment = c.GetOptionalBoolean("applyAlignment", false);
                applyBorder = c.GetOptionalBoolean("applyBorder", false);
                applyFill = c.GetOptionalBoolean("applyFill", false);
                applyFont = c.GetOptionalBoolean("applyFont", false);
                applyNumberFormat = c.GetOptionalBoolean("applyNumberFormat", false);
                applyProtection = c.GetOptionalBoolean("applyProtection", false);
                var styleSheet = c.GetData<StyleSheet>();
                Contract.Assert(styleSheet != null);
                var fonts = c.GetData<List<Font>>();
                var fills = c.GetData<List<Fill>>();
                var borders = c.GetData<List<Borders>>();
                Contract.Assert(styleSheet != null);
                Contract.Assert(fonts != null);
                Contract.Assert(fills != null);
                Contract.Assert(borders != null);


                if (
                    c.GetOptionalString("numFmtId") != null)
                    Format = styleSheet.getFormat(c.GetInteger("numFmtId"));
                if ((c.GetOptionalString("fontId") != null))
                    _font = fonts[c.GetInteger("fontId")];
                {
                    if (c.GetOptionalString("fillId") != null)
                        _background = fills[c.GetInteger("fillId")];
                    else
                    {
                        _background = null;
                    }
                }
                if (c.GetOptionalString("borderId") != null)
                    _borders = borders[c.GetInteger("borderId")];


                using (new StableApplyAttributes(this))
                {
                    if (c.ReaderLocalName == "alignment")
                    {
                        using (ReadContext c2 = c.Read("alignment"))
                        {
                            _textRotation = c2.GetOptionalInteger("textRotation", 0);
                            _horizontalAlignment = c2.GetOptionalEnum<HorizontalCellAlignment>("horizontal");
                            _verticalAlignment = c2.GetOptionalEnum<VerticalCellAlignment>("vertical");
                            if (c2.GetOptionalBoolean("wrapText", false))
                            {
                                _textWrapping = TextWrapping.Wrap;
                            }
                            _indentation = c2.GetOptionalInteger("indent", 0);
                        }
                    }
                    if (c.ReaderLocalName == "protection")
                    {
                        using (ReadContext c2 = c.Read("protection"))
                        {
                            IsLocked = c2.GetOptionalBoolean("locked", IsLockedDefault);
                            IsHidden = c2.GetOptionalBoolean("hidden", IsHiddenDefault);
                        }
                    }
                }
            }
        }

        void IOfficeXmlSerializable.WriteXml(WriteContext context)
        {
            HashList<Fill> fills = context.GetData<HashList<Fill>>();
            Contract.Assert(fills != null);
            var stylestyleRecords = context.GetData<Dictionary<FormattingRecord, int>>();
            Contract.Assert(stylestyleRecords != null);
            var borders = context.GetData<Dictionary<Borders, int>>();
            Contract.Assert(borders != null);
            var fonts = context.GetData<Dictionary<Font, int>>();
            Contract.Assert(fonts != null);
            var formats = context.GetData<Dictionary<string, int>>();
            Contract.Assert(formats != null);
            using (var c = context.Write("xf")) 
            {
                if (this._format!= null)
                {
                    int? nid = StandardFormats.GetFormatId(this._format.Code);
                    Contract.Assert((nid == null) || nid.Value >= 0);
                    int id = nid == null ? formats[this._format.Code] : nid.Value;
                    Contract.Assert(id >= 0);
                    c.SetInteger("numFmtId", id);
                }
                if (Parent != null)
                {
                    c.SetInteger("xfId", stylestyleRecords[Parent]);
                }
                if (this._font != null)
                {
                    c.SetInteger("fontId", fonts[this._font]);
                }
                if (this._background != null)
                {
                    c.SetInteger("fillId", fills.GetItem(this._background).Id);
                }
                if (this._borders != null)
                {
                    c.SetInteger("borderId", borders[this._borders]);
                } //\\ tofix

                c.SetOptionalBoolean("pivotButton", pivotButton, false);
                c.SetOptionalBoolean("quotePrefix", quotePrefix, false);

                c.SetOptionalBoolean("applyAlignment", applyAlignment, false);
                c.SetOptionalBoolean("applyBorder", applyBorder, false);
                c.SetOptionalBoolean("applyFill", applyFill, false);
                c.SetOptionalBoolean("applyFont", applyFont, false);
                c.SetOptionalBoolean("applyNumberFormat", applyNumberFormat, false);
                c.SetOptionalBoolean("applyProtection", applyProtection, false);
                if (_horizontalAlignment != null || _verticalAlignment != null || _textWrapping != null)
                {
                    using (WriteContext cAlignment = c.Write("alignment"))
                    {
                        cAlignment.SetOptionalInteger("textRotation", TextRotation, 0);
                        cAlignment.SetOptionalEnum("horizontal", _horizontalAlignment);
                        cAlignment.SetOptionalEnum("vertical", _verticalAlignment);
                        if (_textWrapping != null)
                        {
                            switch (_textWrapping.Value)
                            {
                                case TextWrapping.Wrap:
                                    cAlignment.SetBoolean("wrapText", true);
                                    break;
                                default:
                                    cAlignment.SetBoolean("wrapText", false);
                                    break;
                            }
                        }
                        cAlignment.SetOptionalDouble("indent", _indentation);
                    }
                }
                if (IsLocked != IsLockedDefault || IsHidden != IsHiddenDefault)
                {
                    using (WriteContext c2 = c.Write("protection"))
                    {
                        c2.SetOptionalBoolean("locked", IsLocked, IsLockedDefault);
                        c2.SetOptionalBoolean("hidden", IsHidden, IsHiddenDefault);
                    }
                }
            }
        }

        #endregion

        internal const int VerticalRotation = 255;

        private class StableApplyAttributes : IDisposable
        {
            private readonly bool applyAlignment;
            private readonly bool applyBorder;
            private readonly bool applyFill;
            private readonly bool applyFont;
            private readonly bool applyNumberFormat;
            private readonly bool applyProtection;
            private readonly FormattingRecord record;

            public StableApplyAttributes(FormattingRecord record)
            {
                Contract.Requires(record != null);
                this.record = record;
                applyAlignment = record.applyAlignment;
                applyBorder = record.applyBorder;
                applyFill = record.applyFill;
                applyFont = record.applyFont;
                applyNumberFormat = record.applyNumberFormat;
                applyProtection = record.applyProtection;
            }

            #region IDisposable Members

            public void Dispose()
            {
                record.applyAlignment = applyAlignment;
                record.applyBorder = applyBorder;
                record.applyFill = applyFill;
                record.applyFont = applyFont;
                record.applyNumberFormat = applyNumberFormat;
                record.applyProtection = applyProtection;
                record.ResetHashCode();
            }

            #endregion
        }


        internal void ReadXml(ReadContext context)
        {
            IOfficeXmlSerializable s = this;
            s.ReadXml(context);
        }

        internal static CellFormattingRecord CreateFromParent(StyleFormattingRecord record)
        {
            Contract.Requires(record != null);
            var result = new CellFormattingRecord();
            result.CopyFrom(record);
            result.SetParent ( record);
            return result;
        }
    }
}